package org.hepeng.workx.jdbc;

import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.ConstructorUtils;
import org.hepeng.workx.exception.ApplicationRuntimeException;
import org.hepeng.workx.extension.XLoader;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

/**
 * @author he peng
 */
public class SelectableDataSource implements DataSource {

    public static final String SELECT_STRATEGY_XPOINT_SYSTEM_PROPERTY_NAME = "workx.datasource.select.strategy.xpoint";
    public static final String SELECT_STRATEGY_CLASS_SYSTEM_PROPERTY_NAME = "workx.datasource.select.strategy.class";
    private static String strategyClassName = System.getProperty(SELECT_STRATEGY_CLASS_SYSTEM_PROPERTY_NAME);
    private static SelectDataSourceStrategy strategy = XLoader.getXLoader(SelectDataSourceStrategy.class).getX();

    static {
        initialize();
    }

    private static void initialize() {
        String xpointName = System.getProperty(SELECT_STRATEGY_XPOINT_SYSTEM_PROPERTY_NAME);
        if (StringUtils.isNotBlank(xpointName)) {
            strategy = XLoader.getXLoader(SelectDataSourceStrategy.class).getX(xpointName);
        }

        if (StringUtils.isNotBlank(strategyClassName)) {
            try {
                strategy = (SelectDataSourceStrategy) ConstructorUtils.invokeConstructor(ClassUtils.getClass(strategyClassName));
            } catch (Exception e) {
                throw new ApplicationRuntimeException(SelectableDataSource.class.getName() + " initialize error " , e);
            }
        }
    }

    @Override
    public Connection getConnection() throws SQLException {
        return getDataSource().getConnection();
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return getDataSource().getConnection(username , password);
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        for (DataSourceMetadata dataSourceMetaData : strategy.getAllDataSources()) {
            DataSource dataSource = dataSourceMetaData.getDataSource();
            dataSource.unwrap(iface);
            dataSourceMetaData.setDataSource(dataSource);
            DataSourceMetadataStore.storeDataSourceMetaData(dataSourceMetaData);
        }
        return (T) getDataSource();
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return getDataSource().isWrapperFor(iface);
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return getDataSource().getLogWriter();
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        for (DataSourceMetadata dataSourceMetaData : strategy.getAllDataSources()) {
            DataSource dataSource = dataSourceMetaData.getDataSource();
            dataSource.setLogWriter(out);
            dataSourceMetaData.setDataSource(dataSource);
            DataSourceMetadataStore.storeDataSourceMetaData(dataSourceMetaData);
        }
    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        for (DataSourceMetadata dataSourceMetaData : strategy.getAllDataSources()) {
            DataSource dataSource = dataSourceMetaData.getDataSource();
            dataSource.setLoginTimeout(seconds);
            dataSourceMetaData.setDataSource(dataSource);
            DataSourceMetadataStore.storeDataSourceMetaData(dataSourceMetaData);
        }
    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return getDataSource().getLoginTimeout();
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return getDataSource().getParentLogger();
    }

    private DataSource getDataSource() {
        return strategy.chooseDataSource(null);
    }
}
